"""
主界面模块
整合摄像头、YOLO检测和瞄准线功能的GUI界面
"""

import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import cv2
import numpy as np
from PIL import Image, ImageTk
import threading
import json
import logging
from typing import Optional

from camera_manager import CameraManager
from yolo_detector import YOLODetector
from crosshair import Crosshair

class MainWindow:
    """主窗口类"""
    
    def __init__(self, config_path: str = "config/default_config.json"):
        """
        初始化主窗口
        
        Args:
            config_path: 配置文件路径
        """
        self.config_path = config_path
        
        # 设置日志
        self.logger = logging.getLogger(__name__)
        
        self.config = self.load_config()
        
        # 初始化组件
        self.camera_manager: Optional[CameraManager] = None
        self.yolo_detector: Optional[YOLODetector] = None
        self.crosshair: Optional[Crosshair] = None
        
        # GUI组件
        self.root: Optional[tk.Tk] = None
        self.video_label: Optional[tk.Label] = None
        self.control_frame: Optional[tk.Frame] = None
        
        # 状态变量
        self.is_running = False
        self.current_frame = None
        self.detection_enabled = True
        self.crosshair_enabled = True
        
        # 初始化GUI
        self.setup_gui()
        self.setup_components()
        
        # 初始化摄像头设备列表
        self.refresh_camera_devices()
        
    def load_config(self) -> dict:
        """加载配置文件"""
        try:
            with open(self.config_path, 'r', encoding='utf-8') as f:
                config = json.load(f)
            self.logger.info(f"配置文件加载成功: {self.config_path}")
            return config
        except Exception as e:
            self.logger.error(f"配置文件加载失败: {e}")
            return self.get_default_config()
    
    def get_default_config(self) -> dict:
        """获取默认配置"""
        return {
            "camera": {"device_id": 0, "width": 640, "height": 480, "fps": 30},
            "yolo": {"model_path": "yolo11n.pt", "confidence": 0.5},
            "crosshair": {"style": "cross", "color": [0, 255, 0], "size": 20},
            "display": {"window_width": 800, "window_height": 600}
        }
    
    def setup_gui(self):
        """设置GUI界面"""
        self.root = tk.Tk()
        self.root.title("反无人机瞄准辅助系统")
        self.root.geometry("1200x800")  # 增加窗口尺寸，提供更好的显示效果
        self.root.resizable(True, True)
        self.root.minsize(800, 600)  # 设置最小窗口尺寸
        
        # 设置窗口图标（如果有的话）
        try:
            # self.root.iconbitmap("icon.ico")
            pass
        except:
            pass
        
        # 创建主框架 - 使用Grid布局更好地控制比例
        main_frame = ttk.Frame(self.root)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 配置Grid权重，确保视频区域和控制面板的比例合理
        main_frame.grid_columnconfigure(0, weight=3)  # 视频区域占3/4
        main_frame.grid_columnconfigure(1, weight=0)  # 控制面板固定宽度
        main_frame.grid_rowconfigure(0, weight=1)
        
        # 视频显示区域
        self.setup_video_area(main_frame)
        
        # 控制面板
        self.setup_control_panel(main_frame)
        
        # 状态栏
        self.setup_status_bar()
        
        # 绑定事件
        self.setup_events()
    
    def setup_video_area(self, parent):
        """设置视频显示区域"""
        video_frame = ttk.LabelFrame(parent, text="视频显示", padding=10)
        # 使用Grid布局，占据第0列
        video_frame.grid(row=0, column=0, sticky="nsew", padx=(0, 10))
        
        # 视频标签 - 直接使用整个区域，让图像自适应
        self.video_label = tk.Label(video_frame, bg="black", text="摄像头未启动")
        self.video_label.pack(fill=tk.BOTH, expand=True)
        
        # 绑定鼠标点击事件（用于校准）
        self.video_label.bind("<Button-1>", self.on_video_click)
    
    def setup_control_panel(self, parent):
        """设置控制面板"""
        # 创建右侧控制面板容器 - 固定宽度，防止被挤压
        control_container = ttk.Frame(parent, width=300)
        # 使用Grid布局，占据第1列，固定宽度
        control_container.grid(row=0, column=1, sticky="ns", padx=(10, 0))
        control_container.grid_propagate(False)  # 防止子组件改变容器大小
        
        # 创建可滚动的控制面板
        canvas = tk.Canvas(control_container, width=280, highlightthickness=0)
        scrollbar = ttk.Scrollbar(control_container, orient="vertical", command=canvas.yview)
        self.control_frame = ttk.Frame(canvas)
        
        # 配置滚动
        self.control_frame.bind(
            "<Configure>",
            lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
        )
        
        canvas.create_window((0, 0), window=self.control_frame, anchor="nw")
        canvas.configure(yscrollcommand=scrollbar.set)
        
        # 布局滚动组件
        canvas.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")
        
        # 鼠标滚轮绑定
        def _on_mousewheel(event):
            canvas.yview_scroll(int(-1*(event.delta/120)), "units")
        canvas.bind_all("<MouseWheel>", _on_mousewheel)
        
        # 摄像头控制
        self.setup_camera_controls()
        
        # YOLO检测控制
        self.setup_yolo_controls()
        
        # 瞄准线控制
        self.setup_crosshair_controls()
        
        # 校准控制
        self.setup_calibration_controls()
        
        # 系统控制
        self.setup_system_controls()
    
    def setup_camera_controls(self):
        """设置摄像头控制"""
        camera_frame = ttk.LabelFrame(self.control_frame, text="摄像头控制", padding=10)
        camera_frame.pack(fill=tk.X, pady=(0, 10))
        
        # 摄像头设备选择
        device_frame = ttk.Frame(camera_frame)
        device_frame.pack(fill=tk.X, pady=(0, 5))
        
        ttk.Label(device_frame, text="摄像头设备:").pack(side=tk.LEFT)
        self.camera_device_var = tk.StringVar()
        self.camera_device_combo = ttk.Combobox(device_frame, textvariable=self.camera_device_var, 
                                               state="readonly", width=15)
        self.camera_device_combo.pack(side=tk.RIGHT, padx=(5, 0))
        self.camera_device_combo.bind("<<ComboboxSelected>>", self.on_camera_device_changed)
        
        # 刷新设备列表按钮
        refresh_frame = ttk.Frame(camera_frame)
        refresh_frame.pack(fill=tk.X, pady=(0, 5))
        
        self.refresh_devices_button = ttk.Button(refresh_frame, text="刷新设备列表", 
                                               command=self.refresh_camera_devices)
        self.refresh_devices_button.pack(side=tk.RIGHT)
        
        # 启动/停止按钮
        self.start_button = ttk.Button(camera_frame, text="启动摄像头", 
                                      command=self.toggle_camera)
        self.start_button.pack(fill=tk.X, pady=(0, 5))
        
        # 摄像头信息
        self.camera_info_label = ttk.Label(camera_frame, text="摄像头未连接")
        self.camera_info_label.pack(fill=tk.X)
    
    def setup_yolo_controls(self):
        """设置YOLO检测控制"""
        yolo_frame = ttk.LabelFrame(self.control_frame, text="目标检测", padding=10)
        yolo_frame.pack(fill=tk.X, pady=(0, 10))
        
        # 检测开关
        self.detection_var = tk.BooleanVar(value=True)
        detection_check = ttk.Checkbutton(yolo_frame, text="启用目标检测", 
                                         variable=self.detection_var,
                                         command=self.toggle_detection)
        detection_check.pack(fill=tk.X, pady=(0, 5))
        
        # 置信度调节
        ttk.Label(yolo_frame, text="置信度阈值:").pack(fill=tk.X)
        self.confidence_var = tk.DoubleVar(value=0.5)
        confidence_scale = ttk.Scale(yolo_frame, from_=0.1, to=1.0, 
                                   variable=self.confidence_var,
                                   command=self.update_confidence)
        confidence_scale.pack(fill=tk.X, pady=(0, 5))
        
        self.confidence_label = ttk.Label(yolo_frame, text="0.50")
        self.confidence_label.pack(fill=tk.X)
    
    def setup_crosshair_controls(self):
        """设置瞄准线控制"""
        crosshair_frame = ttk.LabelFrame(self.control_frame, text="瞄准线", padding=10)
        crosshair_frame.pack(fill=tk.X, pady=(0, 10))
        
        # 瞄准线开关
        self.crosshair_var = tk.BooleanVar(value=True)
        crosshair_check = ttk.Checkbutton(crosshair_frame, text="显示瞄准线", 
                                         variable=self.crosshair_var,
                                         command=self.toggle_crosshair)
        crosshair_check.pack(fill=tk.X, pady=(0, 5))
        
        # 瞄准线样式
        ttk.Label(crosshair_frame, text="样式:").pack(fill=tk.X)
        self.style_var = tk.StringVar(value="cross")
        style_combo = ttk.Combobox(crosshair_frame, textvariable=self.style_var,
                                  values=["cross", "dot", "circle"],
                                  state="readonly")
        style_combo.pack(fill=tk.X, pady=(0, 5))
        style_combo.bind("<<ComboboxSelected>>", self.update_crosshair_style)
        
        # 瞄准线大小
        ttk.Label(crosshair_frame, text="大小:").pack(fill=tk.X)
        self.size_var = tk.IntVar(value=20)
        size_scale = ttk.Scale(crosshair_frame, from_=10, to=50, 
                              variable=self.size_var,
                              command=self.update_crosshair_size)
        size_scale.pack(fill=tk.X)
    
    def setup_calibration_controls(self):
        """设置校准控制"""
        cal_frame = ttk.LabelFrame(self.control_frame, text="瞄准线校准", padding=10)
        cal_frame.pack(fill=tk.X, pady=(0, 10))
        
        # 校准按钮
        self.calibrate_button = ttk.Button(cal_frame, text="开始校准", 
                                          command=self.start_calibration)
        self.calibrate_button.pack(fill=tk.X, pady=(0, 5))
        
        # 校准状态
        self.calibration_status = ttk.Label(cal_frame, text="未校准")
        self.calibration_status.pack(fill=tk.X, pady=(0, 5))
        
        # 保存/加载校准
        button_frame = ttk.Frame(cal_frame)
        button_frame.pack(fill=tk.X)
        
        ttk.Button(button_frame, text="保存", 
                  command=self.save_calibration).pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 2))
        ttk.Button(button_frame, text="加载", 
                  command=self.load_calibration).pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(2, 0))
    
    def setup_system_controls(self):
        """设置系统控制"""
        sys_frame = ttk.LabelFrame(self.control_frame, text="系统", padding=10)
        sys_frame.pack(fill=tk.X, pady=(0, 10))
        
        # 重置按钮
        ttk.Button(sys_frame, text="重置设置", 
                  command=self.reset_settings).pack(fill=tk.X, pady=(0, 5))
        
        # 退出按钮
        ttk.Button(sys_frame, text="退出程序", 
                  command=self.quit_application).pack(fill=tk.X)
    
    def setup_status_bar(self):
        """设置状态栏"""
        self.status_bar = ttk.Label(self.root, text="就绪", relief=tk.SUNKEN)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
    
    def setup_events(self):
        """设置事件绑定"""
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
        
        # 键盘快捷键
        self.root.bind("<space>", lambda e: self.toggle_camera())
        self.root.bind("<c>", lambda e: self.start_calibration())
        self.root.bind("<r>", lambda e: self.reset_settings())
    
    def setup_components(self):
        """初始化组件"""
        # 摄像头管理器
        camera_config = self.config.get("camera", {})
        self.camera_manager = CameraManager(
            camera_id=camera_config.get("device_id", 0),
            width=camera_config.get("width", 640),
            height=camera_config.get("height", 480)
        )
        
        # YOLO检测器
        yolo_config = self.config.get("yolo", {})
        self.yolo_detector = YOLODetector(
            model_path=yolo_config.get("model_path", "yolo11n.pt"),
            confidence=yolo_config.get("confidence", 0.5)
        )
        
        # 瞄准线
        crosshair_config = self.config.get("crosshair", {})
        self.crosshair = Crosshair(
            frame_width=camera_config.get("width", 640),
            frame_height=camera_config.get("height", 480)
        )
        
        # 初始化YOLO检测器
        if not self.yolo_detector.initialize():
            messagebox.showwarning("警告", "YOLO模型初始化失败，检测功能可能不可用")
        
        # 尝试训练靶子检测模型（如果数据集存在）
        self.check_and_train_target_model()
    
    def check_and_train_target_model(self):
        """检查并训练靶子检测模型"""
        try:
            import os
            dataset_yaml = "datasets/targets/target.yaml"
            model_path = "models/target_detection.pt"
            
            # 检查是否已有训练好的模型
            if os.path.exists(model_path):
                self.logger.info("发现已训练的靶子检测模型")
                return
            
            # 检查数据集是否存在
            if not os.path.exists(dataset_yaml):
                self.logger.warning("靶子数据集不存在，跳过模型训练")
                return
            
            # 检查训练数据是否存在
            train_images_dir = "datasets/targets/images/train"
            if not os.path.exists(train_images_dir) or len(os.listdir(train_images_dir)) == 0:
                self.logger.warning("训练图片不存在，跳过模型训练")
                return
            
            # 询问用户是否要训练模型
            result = messagebox.askyesno(
                "训练靶子检测模型",
                "检测到靶子数据集，是否要训练专用的靶子检测模型？\n"
                "训练可能需要几分钟时间，但会提高靶子检测精度。"
            )
            
            if result:
                self.train_target_model_async()
                
        except Exception as e:
            self.logger.error(f"检查靶子模型时出错: {e}")
    
    def train_target_model_async(self):
        """异步训练靶子检测模型"""
        def train_worker():
            try:
                self.logger.info("开始训练靶子检测模型...")
                
                # 在状态栏显示训练信息
                self.root.after(0, lambda: self.update_status("正在训练靶子检测模型..."))
                
                # 训练模型（使用较少的epochs进行快速训练）
                success = self.yolo_detector.train_target_model(
                    dataset_yaml="datasets/targets/target.yaml",
                    epochs=50,  # 减少训练轮数以节省时间
                    imgsz=640
                )
                
                if success:
                    self.root.after(0, lambda: messagebox.showinfo(
                        "训练完成", 
                        "靶子检测模型训练完成！\n现在可以使用专用模型进行更精确的靶子检测。"
                    ))
                    self.root.after(0, lambda: self.update_status("靶子检测模型训练完成"))
                else:
                    self.root.after(0, lambda: messagebox.showerror(
                        "训练失败", 
                        "靶子检测模型训练失败，将使用通用模型。"
                    ))
                    self.root.after(0, lambda: self.update_status("模型训练失败"))
                    
            except Exception as e:
                self.logger.error(f"训练靶子模型时出错: {e}")
                self.root.after(0, lambda: messagebox.showerror(
                    "训练错误", 
                    f"训练过程中出现错误: {e}"
                ))
                self.root.after(0, lambda: self.update_status("模型训练出错"))
        
        # 在后台线程中训练
        train_thread = threading.Thread(target=train_worker, daemon=True)
        train_thread.start()
    
    def toggle_camera(self):
        """切换摄像头状态"""
        if not self.is_running:
            self.start_camera()
        else:
            self.stop_camera()
    
    def start_camera(self):
        """启动摄像头"""
        if not self.camera_manager.initialize():
            messagebox.showerror("错误", "摄像头初始化失败")
            return
        
        if not self.camera_manager.start_capture(self.process_frame):
            messagebox.showerror("错误", "摄像头启动失败")
            return
        
        self.is_running = True
        self.start_button.config(text="停止摄像头")
        self.update_status("摄像头已启动")
        
        # 更新摄像头信息
        info = self.camera_manager.get_camera_info()
        info_text = f"{info.get('width', 0)}x{info.get('height', 0)} @ {info.get('fps', 0):.1f}fps"
        self.camera_info_label.config(text=info_text)
    
    def stop_camera(self):
        """停止摄像头"""
        if self.is_running:
            self.camera_manager.stop_capture()
            self.camera_manager.release()
            self.is_running = False
            self.start_button.config(text="启动摄像头")
            self.update_status("摄像头已停止")
            self.logger.info("摄像头已停止")
            
            # 清空视频显示
            self.video_label.config(image="", text="摄像头未启动")
            self.camera_info_label.config(text="摄像头未连接")
    
    def process_frame(self, frame):
        """处理视频帧"""
        try:
            processed_frame = frame.copy()
            
            # YOLO检测
            detections = []
            if self.detection_enabled and self.yolo_detector:
                # 首先尝试使用专用的靶子检测模型
                target_detections = self.yolo_detector.detect_targets(frame)
                detections.extend(target_detections)
                
                # 如果没有检测到靶子，或者用户启用了通用检测，也使用通用模型
                general_detections = self.yolo_detector.detect(frame)
                # 过滤掉非人员的通用检测结果，避免重复
                person_detections = [d for d in general_detections if d.get('class_name') == 'person']
                detections.extend(person_detections)
                
                if detections:
                    processed_frame = self.yolo_detector.draw_detections(processed_frame, detections)
            
            # 绘制瞄准线
            if self.crosshair_enabled and self.crosshair:
                processed_frame = self.crosshair.draw(processed_frame)
            
            # 更新显示
            self.update_video_display(processed_frame)
            
            # 保存当前帧
            self.current_frame = processed_frame
            
        except Exception as e:
            self.logger.error(f"处理视频帧时出错: {e}")
    
    def update_video_display(self, frame):
        """更新视频显示"""
        try:
            # 转换颜色空间
            frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            
            # 转换为PIL图像
            pil_image = Image.fromarray(frame_rgb)
            
            # 获取视频标签的实际尺寸
            label_width = self.video_label.winfo_width()
            label_height = self.video_label.winfo_height()
            
            # 如果标签尺寸有效，按比例缩放图像，保持纵横比
            if label_width > 50 and label_height > 50:  # 确保有足够的显示空间
                # 计算缩放比例，保持纵横比
                img_width, img_height = pil_image.size
                scale_w = label_width / img_width
                scale_h = label_height / img_height
                scale = min(scale_w, scale_h)  # 选择较小的缩放比例
                
                # 确保最小缩放比例，避免图像过小
                min_scale = 0.3  # 最小缩放到原尺寸的30%
                scale = max(scale, min_scale)
                
                # 计算新的尺寸
                new_width = int(img_width * scale)
                new_height = int(img_height * scale)
                
                # 调整图像大小，保持纵横比
                pil_image = pil_image.resize((new_width, new_height), Image.Resampling.LANCZOS)
            else:
                # 如果标签尺寸太小，使用默认尺寸
                pil_image = pil_image.resize((640, 480), Image.Resampling.LANCZOS)
            
            # 转换为Tkinter图像
            tk_image = ImageTk.PhotoImage(pil_image)
            
            # 更新显示
            self.video_label.config(image=tk_image, text="")
            self.video_label.image = tk_image  # 保持引用
            
        except Exception as e:
            self.logger.error(f"更新视频显示时出错: {e}")
    
    def toggle_detection(self):
        """切换检测状态"""
        self.detection_enabled = self.detection_var.get()
        status = "启用" if self.detection_enabled else "禁用"
        self.update_status(f"目标检测已{status}")
    
    def toggle_crosshair(self):
        """切换瞄准线显示"""
        self.crosshair_enabled = self.crosshair_var.get()
        status = "显示" if self.crosshair_enabled else "隐藏"
        self.update_status(f"瞄准线已{status}")
    
    def update_confidence(self, value):
        """更新置信度阈值"""
        confidence = float(value)
        if self.yolo_detector:
            self.yolo_detector.set_confidence_threshold(confidence)
        self.confidence_label.config(text=f"{confidence:.2f}")
    
    def update_crosshair_style(self, event=None):
        """更新瞄准线样式"""
        if self.crosshair:
            self.crosshair.set_style(self.style_var.get())
    
    def update_crosshair_size(self, value):
        """更新瞄准线大小"""
        if self.crosshair:
            self.crosshair.size = int(float(value))
    
    def start_calibration(self):
        """开始校准"""
        if not self.crosshair:
            return
        
        if not self.crosshair.calibration_mode:
            self.crosshair.start_calibration()
            self.calibrate_button.config(text="完成校准")
            self.calibration_status.config(text="校准中... 点击目标位置")
            self.update_status("校准模式已启动，请点击目标位置")
        else:
            self.crosshair.finish_calibration()
            self.calibrate_button.config(text="开始校准")
            self.calibration_status.config(text="校准完成")
            self.update_status("校准已完成")
    
    def on_video_click(self, event):
        """处理视频区域点击事件"""
        if self.crosshair and self.crosshair.calibration_mode:
            # 计算实际图像坐标
            x = event.x
            y = event.y
            
            # 添加校准点
            self.crosshair.add_calibration_point(x, y)
            
            # 更新状态
            point_count = len(self.crosshair.calibration_points)
            self.calibration_status.config(text=f"校准点: {point_count}/3")
            
            if point_count >= 3:
                self.start_calibration()  # 完成校准
    
    def save_calibration(self):
        """保存校准数据"""
        if not self.crosshair:
            return
        
        filename = filedialog.asksaveasfilename(
            title="保存校准数据",
            defaultextension=".json",
            filetypes=[("JSON files", "*.json"), ("All files", "*.*")]
        )
        
        if filename:
            self.crosshair.save_calibration(filename)
            self.update_status(f"校准数据已保存: {filename}")
    
    def load_calibration(self):
        """加载校准数据"""
        if not self.crosshair:
            return
        
        filename = filedialog.askopenfilename(
            title="加载校准数据",
            filetypes=[("JSON files", "*.json"), ("All files", "*.*")]
        )
        
        if filename:
            if self.crosshair.load_calibration(filename):
                self.update_status(f"校准数据已加载: {filename}")
                self.calibration_status.config(text="已加载校准")
            else:
                messagebox.showerror("错误", "校准数据加载失败")
    
    def reset_settings(self):
        """重置设置"""
        if messagebox.askyesno("确认", "确定要重置所有设置吗？"):
            if self.crosshair:
                self.crosshair.reset()
            
            # 重置GUI控件
            self.confidence_var.set(0.5)
            self.style_var.set("cross")
            self.size_var.set(20)
            self.detection_var.set(True)
            self.crosshair_var.set(True)
            
            self.calibration_status.config(text="未校准")
            self.update_status("设置已重置")
    
    def update_status(self, message: str):
        """更新状态栏"""
        self.status_bar.config(text=message)
        self.logger.info(message)
    
    def on_closing(self):
        """窗口关闭事件"""
        try:
            # 停止摄像头
            if self.is_running:
                self.stop_camera()
            
            # 保存配置
            self.save_config()
            
            # 释放资源
            if hasattr(self, 'camera_manager'):
                self.camera_manager.release()
            
            self.logger.info("应用程序正常退出")
            
        except Exception as e:
            self.logger.error(f"退出时发生错误: {e}")
        finally:
            self.root.quit()
            self.root.destroy()
    
    def quit_application(self):
        """退出应用程序"""
        if messagebox.askyesno("确认", "确定要退出程序吗？"):
            # 停止摄像头
            if self.is_running:
                self.stop_camera()
            
            # 释放资源
            if self.camera_manager:
                self.camera_manager.release()
            
            # 关闭窗口
            self.root.quit()
            self.root.destroy()
    
    def detect_available_cameras(self) -> list:
        """检测可用的摄像头设备"""
        available_cameras = []
        
        # 测试摄像头ID 0-9
        for camera_id in range(10):
            try:
                cap = cv2.VideoCapture(camera_id)
                if cap.isOpened():
                    # 尝试读取一帧来确认摄像头真正可用
                    ret, _ = cap.read()
                    if ret:
                        # 获取摄像头信息
                        width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
                        height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
                        fps = cap.get(cv2.CAP_PROP_FPS)
                        
                        camera_info = {
                            'id': camera_id,
                            'name': f"摄像头 {camera_id} ({width}x{height})",
                            'width': width,
                            'height': height,
                            'fps': fps
                        }
                        available_cameras.append(camera_info)
                        self.logger.info(f"检测到摄像头 {camera_id}: {width}x{height}")
                cap.release()
            except Exception as e:
                self.logger.debug(f"摄像头 {camera_id} 不可用: {e}")
                continue
        
        return available_cameras
    
    def refresh_camera_devices(self):
        """刷新摄像头设备列表"""
        self.update_status("正在检测摄像头设备...")
        
        # 在后台线程中检测摄像头，避免阻塞UI
        def detect_in_background():
            try:
                available_cameras = self.detect_available_cameras()
                
                # 在主线程中更新UI
                self.root.after(0, lambda: self._update_camera_list(available_cameras))
            except Exception as e:
                self.logger.error(f"检测摄像头设备时出错: {e}")
                self.root.after(0, lambda: self.update_status("摄像头设备检测失败"))
        
        # 启动后台检测线程
        threading.Thread(target=detect_in_background, daemon=True).start()
    
    def _update_camera_list(self, available_cameras):
        """更新摄像头设备列表（在主线程中调用）"""
        if not hasattr(self, 'camera_device_combo'):
            return
        
        # 清空现有选项
        self.camera_device_combo['values'] = []
        
        if available_cameras:
            # 添加可用摄像头到下拉列表
            camera_names = [camera['name'] for camera in available_cameras]
            self.camera_device_combo['values'] = camera_names
            
            # 保存摄像头信息以供后续使用
            self.available_cameras = available_cameras
            
            # 设置默认选择
            current_camera_id = getattr(self.camera_manager, 'camera_id', 0) if self.camera_manager else 0
            default_index = 0
            for i, camera in enumerate(available_cameras):
                if camera['id'] == current_camera_id:
                    default_index = i
                    break
            
            self.camera_device_combo.current(default_index)
            self.update_status(f"检测到 {len(available_cameras)} 个摄像头设备")
        else:
            self.available_cameras = []
            self.update_status("未检测到可用的摄像头设备")
    
    def on_camera_device_changed(self, event=None):
        """摄像头设备选择改变时的回调"""
        if not hasattr(self, 'available_cameras') or not self.available_cameras:
            return
        
        selected_index = self.camera_device_combo.current()
        if 0 <= selected_index < len(self.available_cameras):
            selected_camera = self.available_cameras[selected_index]
            
            # 如果摄像头正在运行，需要先停止
            was_running = self.is_running
            if was_running:
                self.stop_camera()
            
            # 更新摄像头管理器的设备ID
            if self.camera_manager:
                self.camera_manager.camera_id = selected_camera['id']
                self.logger.info(f"切换到摄像头设备 {selected_camera['id']}: {selected_camera['name']}")
            
            # 更新配置
            if 'camera' not in self.config:
                self.config['camera'] = {}
            self.config['camera']['device_id'] = selected_camera['id']
            
            # 保存配置到文件
            self.save_config()
            
            # 如果之前在运行，重新启动摄像头
            if was_running:
                self.start_camera()
            
            self.update_status(f"已切换到 {selected_camera['name']}")
    
    def save_config(self):
        """保存配置到文件"""
        try:
            import os
            # 确保配置目录存在
            config_dir = os.path.dirname(self.config_path)
            if config_dir and not os.path.exists(config_dir):
                os.makedirs(config_dir)
            
            with open(self.config_path, 'w', encoding='utf-8') as f:
                json.dump(self.config, f, indent=2, ensure_ascii=False)
            self.logger.info(f"配置已保存到 {self.config_path}")
        except Exception as e:
            self.logger.error(f"保存配置失败: {e}")
    
    def run(self):
        """运行主程序"""
        self.logger.info("启动反无人机瞄准辅助系统")
        self.root.mainloop()

def main():
    """主函数"""
    # 设置日志
    logging.basicConfig(
        level=logging.INFO,
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
    )
    
    # 创建并运行主窗口
    app = MainWindow()
    app.run()

if __name__ == "__main__":
    main()